<?php
// +-----------------------------------------------------------------------+
// |                                                                       |
// | WebFormsQA     (Question - Answer)                                    |
// | Copyright  (C) 2010                                                   |
// | Official site  www.scale-tech.ru                                      |
// | email          scale-tech@mail.ru                                     |
// | autor          Alexander Demachev                                     |
// |                                                                       |
// | This program is free software; you can redistribute it and/or modify  |
// | it under the terms of the GNU General Public License as published by  |
// | the Free Software Foundation; version 2 of the License.               |
// |                                                                       |
// | This program is distributed in the hope that it will be useful,       |
// | but WITHOUT ANY WARRANTY; without even the implied warranty of        |
// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         |
// | GNU General Public License for more details.                          |
// +-----------------------------------------------------------------------+

/** Типы операций */
define('TYPE_DELETE', 1);       //!< удаление записи
define('TYPE_DISABLE', 2);      //!< выключение записи из анализа

require_once(BASE_DIR.'database.class.php');
require_once(CONFIG_DIR."tables.inc.php");


/**
 * @page StructMySql Структура базы данных МySql
 * 
 * @dot
 * 		digraph tablesMySql {
 *			nodesep=1.2;
 *			ranksep=1.2;
 *			node [shape=record];
 *			category [shape=record,label="{Таблица \"категории\"|<a1>categoryID - primary key|name VARCHAR(255)|parent INT|description TEXT|count INT}"];
 *			question [shape=record,label="{Таблица \"вопросы\"|ID - primary key|<a1>categoryID INT|question TEXT|answer TEXT|time_question INT|time_answer INT|email VARCHAR(120)|enabled INT}"];
 *			users [shape=record,label="{Таблица \"пользователи\"|ID - primary key|login VARCHAR(32)|pass VARCHAR(32)|email VARCHAR(32)|admin BOOL}"];
 *
 * 			question:a1 -> category:a1;
 *		}
 * @enddot
 *
 * Класс для работы с базой данных проекта WebFormsQA @ref QuestionBase
 * 
 */

/*!
 * @page QuestionBasePage Исходный код класса QuestionBase
 * @include question_base.class.php
 */

/*! @addtogroup ClassWebFormsQA Основные классы проекта
 *  Группа включает основные классы используемые в проекте WebFormsQA
 *  @{
 */


/**
 * @class QuestionBase
 * @brief  Класс предназначен для работы с базой данных MySql проекта - "WebFormsQA" \n
 * @ref StructMySql \n
 * @ref QuestionBasePage \n
 */
class QuestionBase extends Database {

	/**
     * @brief 	Метод создает таблицы базы данных, если они не существуют
     * 
     * @param string &$error		- список ошибок, возникших при создании таблиц
     * @param bool 	$flagUtf8=true  - флаг указания кодировки UTF-8 и способа сравнения слов и букв (COLLATE)
     *                                по умолчанию UTF-8 
     * @return bool             	- успех операции
     *                            	  true - все таблицы созданы
     *                            	  false - все или хотя бы одна из таблиц не создана (существует)
     */
	
	function createTables(&$error, $flagUtf8=true)
	{

		$flag_set="";
		if( $flagUtf8 ) $flag_set=" CHARACTER SET utf8 COLLATE utf8_general_ci";
		else $flag_set=" CHARACTER SET cp1251 COLLATE cp1251_general_ci";
		

		// пробую создать таблицы базы, если они еще не существуют
		// добавляем не существующие таблицы
	    $flag1 = $this->db_query("
	        CREATE TABLE IF NOT EXISTS ".CATEGORIES_TABLE." (categoryID INT PRIMARY KEY AUTO_INCREMENT,
	        name VARCHAR(255), parent INT, description TEXT, count INT )".$flag_set
	    );
	    if( !$flag1 ) $error = $this->db_error();
	    
	    $flag2 = $this->db_query("
	        CREATE TABLE IF NOT EXISTS ".QUESTION_TABLE." (ID INT PRIMARY KEY AUTO_INCREMENT,
	        categoryID INT,question TEXT, answer TEXT, time_question INT, time_answer INT,
	        email VARCHAR(120), enabled INT )"
	    );
	    if( !$flag2 ) $error += $this->db_error();

	    
	    $flag3 = $this->db_query("
	    	CREATE TABLE IF NOT EXISTS ".USERS_TABLE." (ID INT PRIMARY KEY AUTO_INCREMENT,
            login VARCHAR(32), pass VARCHAR(32), email VARCHAR(32),  admin BOOL)".$flag_set
	    );
	    if( !$flag3 ) $error += $this->db_error();
	    
	    return ($flag1 && $flag2 && $flag3);
	}
	
	
    /**
     * @brief Метод добавляет в таблицу USER_TABLE данные пользователя с уникальным
     *        именем(login), если имя уже есть в таблице, то возвращается false
     *
     * @param array &$data          - данные для добавления
     *                                ключи login pass email admin
     * @param integer &$id          - идентификатор найденного или добавленной записи
     * @return boolean              - успех операции
     */
    function addUser(&$data, &$id)
    {
        $flag_result = false;
        $id = 0;
        if( extract($data) ) {

            // ищем login
            $id = $this->searchNameInTable(USERS_TABLE, "login", $login);

            if( !$id ) {
                // такого пользователя еще нет операция добавления
                // в базе храним не пароль, а его hash значение
                $pass = md5($pass);
                $this->db_query("
                	INSERT INTO ".USERS_TABLE."( login, pass, email, admin )
                    VALUES ( '$login', '$pass', '$email', '$admin' )"
                ) or die($this->db_error());
                
                $id = $this->db_insert_id();
                $flag_result = true;
            }

        }

        return $flag_result;

    } // end function addUser
    
    /**
     * @brief Метод обновляет таблицу USER_TABLE
     * @param integer $id          - идентификатор найденного или добавленной записи
     * @param array &$data         - данные для обновления
     *                                ключи login pass email admin
     
     * @return boolean              - успех операции
     */
    function updateUser($id, &$data)
    {
        $flag_result = false;
        $id = 0;
        if( extract($data) ) {


        $q = $this->db_query("
        		UPDATE ".$name_table." 
        		SET ".$str." 
        		WHERE ID='$id' LIMIT 1 "
        	) or die($this->db_error());        	
        	
            if( !$id ) {
                // такого пользователя еще нет операция добавления
                // в базе храним не пароль, а его hash значение
                $pass = md5($pass);
                $this->db_query("
                	INSERT INTO ".USERS_TABLE."( login, pass, email, admin )
                    VALUES ( '$login', '$pass', '$email', '$admin' )"
                ) or die($this->db_error());
                
                $id = $this->db_insert_id();
                $flag_result = true;
            }

        }

        return $flag_result;

    } // end function addUser    
    

    /**
     * @brief Метод извлекает данные пользователя из таблицы USER_TABLE
     *        по идентификатору пользователя или имени
     *
     * @param array &$data          - данные для добавлени
     *                                ключи login email admin
     * @param integer $id           - идентификатор пользовател
     * @param string  $name         - имя пользовател
     * @return boolean              - успех операции
     */
    function getDataUser(&$data, $id, $name="")
    {
        $flag_result = false;
        $flag_select = true;
        $q = "";
        $data = array();

        if ($id) {
            $q = $this->db_query("SELECT login, email, admin FROM ".
                                  USERS_TABLE." WHERE ID='$id' LIMIT 1 ") or die($this->db_error());
        } elseif ($name) {
            $q = $this->db_query("SELECT login, email, admin FROM ".
                                  USERS_TABLE." WHERE login='$name' LIMIT 1 ") or die($this->db_error());
        } else {
            $flag_select = false;
        }

        if ($flag_select && $row = $this->db_fetch_row($q)) {
            $data["login"]  = $row[0];
            $data["email"]  = $row[1];
            $data["admin"]  = $row[2];            
            $flag_result    = true;
        }

        return $flag_result;
    }
	

    /**
     * @brief Метод добавляет вопрос и email в базу
     *
     * @param integer $id_category  - идентификатор категории
     * @param string $question      - ссылка на строку с вопросом
     * @param string $email         - ссылка на строку с адресом
     * @param integer &$id          - идентификатор добавленного вопроса
     *
     * @return bool                 - успех операции
     */
    function addQuestion($id_category, $question, $email, &$id)
    {
        $id             = 0;
        $flag_result    = false;
        $question       = $this->validate_search_string($question);
        // так как поиск не должен учитывать регистр символов
        // то hash вычисляем для строки в нижнем регистре
        // $hash           = md5(strtolower($question));

        $t       = time();
        $enabled = 1;

        $this->db_query("
            INSERT INTO ".QUESTION_TABLE."(categoryID, question,
            time_question, email, enabled)
            VALUES ('$id_category', '$question',
                '$t', '$email', '$enabled' )"
        ) or die($this->db_error());

        $id             = $this->db_insert_id();
        $flag_result    = true;

        return $flag_result;
    }



    /**
     * @brief Метод запрашивает список вопросов выбранной категории у которых есть ответы
     *
     * @param integer   $id_category    - идентификатор категории
     * @param array     &$out           - результат работы, двумерный массив
     * -                                  где j - все поля записи в таблице QUESTION_TABLE
     * -                                    [0] - ID
     * -                                    [1] - categoryID
     * -                                    [2] - question
     * -                                    [3] - answer
     * -                                    [4] - time_question
     * -                                    [5] - time_answer
     * -                                    [6] - email
     * -                                    [7] - enabled
     * @return integer                  - количество найденный вопросов
     */
    function getCategoryQuestions($id_category, &$out)
    {
        $result = 0;
        $out    = array();

        $q = $this->db_query("
            SELECT *
            FROM ".QUESTION_TABLE."
            WHERE categoryID='$id_category'
                AND enabled=1
                AND answer IS NOT NULL"
        ) or die($this->db_error());

        while ($row = $this->db_fetch_row($q)) {
            $out[] = $row;
        }

        return sizeof($out);
    }
    
    /**
     * @brief Метод запрашивает список всех вопросов у которых есть ответы или нет ответов
     *        в зависимости от $flag_answer
     * @param boolean   $flag_answer    - true - запрашиваем вопросы с ответами, false - без ответов        
     * @param array     &$out           - результат работы, двумерный массив
     *                                    где j - все поля записи QUESTION_TABLE и имя из CATEGORIES_TABLE
     * -                                    [0] - ID
     * -                                    [1] - categoryID
     * -                                    [2] - question
     * -                                    [3] - answer
     * -                                    [4] - time_question
     * -                                    [5] - time_answer
     * -                                    [6] - email
     * -                                    [7] - enabled
     * -                                    [8] - name (название категории)
     * @return integer                  - количество найденный вопросов
     */
    function getAllQuestionsAnswerOrNot($flag_answer, &$out)
    {
        $result = 0;
        $out    = array();
        $answer = "IS NULL";
        
        if( $flag_answer ) $answer = "IS NOT NULL";

        $q = $this->db_query("
            SELECT t1.*, t2.name
            FROM ".QUESTION_TABLE." as t1,
                ".CATEGORIES_TABLE." as t2
            WHERE t1.answer ".$answer." 
                AND t1.enabled=1
                AND t1.categoryID=t2.categoryID
                ORDER BY t2.name"
        ) or die($this->db_error());
        
        while ($row = $this->db_fetch_row($q)) {
            $out[] = $row;
        }

        return sizeof($out);
    }

    /**
     * @brief Метод запрашивает список всех вопросов из таблицы категорий
     *        в зависимости от $flag_answer
     * @param array     &$out           - результат работы, двумерный массив cм. getAllQuestionsAnswerOrNot()
     * @return integer                  - количество найденный вопросов
     */
    function getAllQuestions(&$out)
    {
        $result = 0;
        $out    = array();
        $q = $this->db_query("
            SELECT t1.*, t2.name
            FROM ".QUESTION_TABLE." as t1,
                ".CATEGORIES_TABLE." as t2
            WHERE t1.enabled=1
                AND t1.categoryID=t2.categoryID
                ORDER BY t2.name"
        ) or die($this->db_error());
        
        while ($row = $this->db_fetch_row($q)) {
            $out[] = $row;
        }

        return sizeof($out);
    }

    /**
     * @brief Метод запрашивает список всех вопросов выбранной категории
     * @param array     &$out           - результат работы, двумерный массив cм. getAllQuestionsAnswerOrNot()
     * @return integer                  - количество найденный вопросов
     */
    function getAllCategoryQuestions($id_category, &$out)
    {
        $result = 0;
        $out    = array();
        $q = $this->db_query("
            SELECT t1.*, t2.name
            FROM ".QUESTION_TABLE." as t1,
                ".CATEGORIES_TABLE." as t2
            WHERE t1.categoryID='$id_category'  
                AND t1.enabled=1
                AND t1.categoryID=t2.categoryID
                ORDER BY t2.name"
        ) or die($this->db_error());
        
        while ($row = $this->db_fetch_row($q)) {
            $out[] = $row;
        }
        return sizeof($out);
    }
    
    

    /**
     * @brief Метод меняет категорию вопроса (вопросов)
     *
     * @param $category_new     - новый идентификатор категории
     * @param array $idx        - массив содержащий идентификаторы вопросов
     * @return integer          - количество записей, которые обработали
     */
    function changeCategory($category_new, &$idx)
    {
        $result = 0;
        // формируем запрос по OR
        if ($size = $this->getRequest('ID', $idx, $request, false)) {
            $q = $this->db_query("
                UPDATE ".QUESTION_TABLE."
                SET categoryID='$category_new'
                WHERE ".$request." LIMIT ".$size
            ) or die($this->db_error());

            $result = $this->db_affected_rows();
        }

        return $result;
    }

    /**
     * @brief Метод удаляет вопрос (вопросы).
     *
     * @param array $idx        - массив содержащий идентификаторы вопросов, которые удаляются
     * @param integer $type     - тип операции
     * -                          TYPE_DELETE   - удаление записи
     * -                          TYPE_DISABLE  - выключение записи из анализа
     * @return integer          - количество записей, которые обработали в соответствии с кодом операции
     */
    function delQuestions($type, &$idx)
    {
        $result      = 0;
        $flag_result = false;
        switch ($type) {
            case TYPE_DELETE:
                // формируем строку запроса по OR
                if ($size = $this->getRequest('ID', $idx, $request, false)) {
                    $q = $this->db_query("
                        DELETE FROM ".QUESTION_TABLE."
                        WHERE ".$request." LIMIT ".$size
                    ) or die($this->db_error());

                    $flag_result = true;
                }
                break;

            case TYPE_DISABLE:
                if ($size = $this->getRequest('ID', $idx, $request, false)) {
                    $q = $this->db_query("
                        UPDATE ".QUESTION_TABLE."
                        SET enabled=0
                        WHERE ".$request." LIMIT ".$size
                    ) or die($this->db_error());

                    $flag_result = true;
                }
                break;
        }

        if ($flag_result) {
            $result = $this->db_affected_rows();
        }

        return $result;
    }

    /**
     * @brief Метод добавляет в базу ответ на вопрос в таблицу QUESTION_TABLE
     *
     * @param integer $id        - идентификатор вопроса, для которого добавляем ответ
     * @param string  &$answer   - ссылка на строку с ответом
     *
     * ПРИМЕЧАНИЕ:     Метод удаляет из строки все некорректный символы,
     *                 которые нельзя использовать в строке запроса или экранирует их
     *                 шоб злые хацкеры не беспокоились
     *
     * @return bool    - успех операции
     */
    function addAnswer($id, &$answer)
    {
        $flag_result    = false;
        $answer         = $this->validate_search_string($answer);

        $q = $this->db_query("
            UPDATE ".QUESTION_TABLE."
            SET answer='$answer'
            WHERE ID='$id' LIMIT 1"
        ) or die($this->db_error());

        if ($this->db_affected_rows()) $flag_result = true;

        return $flag_result;
    }

    
    /**
     * @brief Метод ищет категорию в базе по имени
     *
     * @param string  &$category   	- ссылка на категорию, которая ищется
     * @param integer &$id_parent   - идентификатор родительской категории у найденной 
     *
     * @return integer - идентификатор найденной категории
     */
    function findCategory(&$category, &$id_parent )
    {
        $id         = 0;
        $id_parent  = 0;
        $category   = $this->validate_search_string($category);

        $q = $this->db_query("
            SELECT categoryID, parent
            FROM ".CATEGORIES_TABLE."
            WHERE name='$category' LIMIT 1 "
        ) or die($this->db_error());
        
        if( $row = $this->db_fetch_row($q) ) {
        	$id 		= $row[0];	
        	$id_parent 	= $row[1]; 
        } 
        return $id; 
    }
    
    /**
     * @brief Метод добавляет новую категорию в базу
     *
     * @param integer   $id_parent     - идентификатор родительской категории
     * @param string    - &$category   - ссылка на новую категорию
     *
     * ПРИМЕЧАНИЕ:      Категория добавляется при условии ее уникальности
     * -                Метод удаляет из строки все некорректные символы
     *
     * @return integer - идентификатор добавленной категории
     */
    function addCategory($id_parent, &$category, $flag=false)
    {
        $id     = 0;
        $category   = $this->validate_search_string($category);

        $q = $this->db_query("
            SELECT categoryID
            FROM ".CATEGORIES_TABLE."
            WHERE name='$category' LIMIT 1 "
        ) or die($this->db_error());


        if (!($row = $this->db_fetch_row($q))) {
            // категория уникальная
            $this->db_query("
                INSERT INTO ".CATEGORIES_TABLE."(name, parent)
                VALUES ('$category', '$id_parent')"
            ) or die($this->db_error());
			
            $id = $this->db_insert_id();
        } else {

			if( $flag ) {
				$id = $row[0];
			}
		}

        return $id;
    }


    /**
     * @brief Метод удаляет категорию из базы
     *
     * @param integer $category  - идентификатор категории для удаления, если у категории есть родительская,
     *                             то после удаления здесь содержится идентификатор родительской категории
     *
     * ПРИМЕЧАНИЕ:      Категория удаляется в случае если она не содержит в себе ни одной подкатегории т.е. не является родительской
     *                  если в категории содержаться вопросы они переносятся в созданную категорию "удаленные" 
     *
     * @return integer - код удаления
     *                   0 - категория успешно удалена
	 *                   1 - категория удалена, но содержит вопросы, которые перенесли в корневую
     *                   2 - категория содержит под категории, вначале удалите их 
     */
    function delCategory(&$category)
    {
        $res = -1;		
		$categoryParent = 0;

        settype($category, "integer" );
		if( $category == 0 ) return $res;

		// ищу категорию с заданным родительским id т.е. содержит под категории
        $q = $this->db_query("
            SELECT categoryID
            FROM ".CATEGORIES_TABLE."
            WHERE parent='$category' LIMIT 1 "
        ) or die($this->db_error());


		if( $this->db_affected_rows($q) ) {
			$res = 2; 
		} else {

			// явно сохраняю родительскую категорию
			$q = $this->db_query("
					SELECT parent
					FROM ".CATEGORIES_TABLE."
					WHERE categoryId='$category' LIMIT 1 "
				) or die($this->db_error());
			if( $row = $this->db_fetch_row($q) ) $categoryParent = $row[0];

		
			$q = $this->db_query("
				DELETE FROM ".CATEGORIES_TABLE."
				WHERE categoryID='$category' LIMIT 1 "
			) or die($this->db_error());
	

			if( $this->db_affected_rows($q) ) {

				$newName = MESS_DEL_CATEGORY_NAME;
				$idNewCategory = $this->addCategory(0, $newName, true);
				$res = 0;
				// если категория содержит вопросы, переносим их в корневую
				$q = $this->db_query("
					UPDATE ".QUESTION_TABLE."
					SET categoryID='$idNewCategory' 
					WHERE categoryID='$category' "
				) or die($this->db_error());

				if( $this->db_affected_rows($q) ) {
					$res = 1;
				} else {
					$this->db_query("DELETE FROM ".CATEGORIES_TABLE." WHERE categoryID='$idNewCategory' LIMIT 1 " ) or die($this->db_error());
				}
			}

		}

		if( $categoryParent ) $category = $categoryParent;

		return $res;
    }


    
    /**
     * @brief Метод проверяет введенный пароль для зарегистрированного пользователя
     *
     * @param string   $str_password   	- введенный пароль
     * @return bool  					- признак соответствия текущего пароля и строки $str_password
     */
    function checkPassUser($str_password) 
    {
    	$flag = false;
		if( isset($_SESSION['id_user'] )) {
			$id = $_SESSION['id_user'];					
			$q = $this->db_query(
          		"SELECT pass, login, admin, email FROM ".
               	USERS_TABLE.
             	" WHERE ID='$id' LIMIT 1 "
          	) or die( $base->db_error() );
          	
    		if ($row = $this->db_fetch_row($q)) {
   				$pass = $row[0];
	        	// в базе хранится не пароль, а его hash
        		if( !strcmp($pass, md5($str_password)) ) {
					$flag = true;                             
        		}
			}
		}
		
		return $flag;
    }


} // end class QuestionBase

/*! @} */

?>
